#ifndef ROCKET_NET_TCP_CONNECTION_H
#define ROCKET_NET_TCP_CONNECTION_H

#include <memory>
#include <vector>

#include "../coder/abstract_coder.h"
#include "../coder/abstract_protocol.h"
#include "../io_thread.h"
#include "net_addr.h"
#include "tcp_buffer.h"

namespace rocket {

class TcpConnection {
  public:
    typedef std::shared_ptr<TcpConnection> s_ptr;

  public:
    enum TcpState {
        NotConnected = 1,
        Connected = 2,
        HalfClosing = 3,
        Closed = 4,
    };

    enum TcpConnectionType {
        TcpConnectionByServer = 1, // use by server, as a client connection;
        TcpConnectionByClient = 2, // use by client, as a server connection.
    };

  public:
    TcpConnection(EventLoop *event_loop, int fd, int buffer_size,
                  NetAddr::s_ptr local_addr, NetAddr::s_ptr peer_addr,
                  TcpConnectionType type = TcpConnectionByServer);

    ~TcpConnection();

    void onRead();

    void execute();

    void onWrite();

    void clear();

    void shutDown();

    void setState(const TcpConnection::TcpState state);

    TcpConnection::TcpState getState();

    void setConnectionType(TcpConnectionType type);

    // start listen write_event
    void listenWrite();

    void listenRead();

    void pushSendMessage(AbstractProtocol::s_ptr,
                         std::function<void(AbstractProtocol::s_ptr)>);

    void pushReadMessage(const std::string &,
                         std::function<void(AbstractProtocol::s_ptr)>);

    NetAddr::s_ptr getLocalAddr();
    NetAddr::s_ptr getPeerAddr();

  private:
    EventLoop *m_event_loop{nullptr};

    NetAddr::s_ptr m_local_addr;
    NetAddr::s_ptr m_peer_addr;

    TcpBuffer::s_ptr m_in_buffer;
    TcpBuffer::s_ptr m_out_buffer;

    FdEvent *m_fd_event{nullptr};

    TcpState m_state;
    int m_fd{0};

    TcpConnectionType m_connection_type;

    // key = AbstractProtocol::m_req_id
    std::vector<std::pair<AbstractProtocol::s_ptr,
                          std::function<void(AbstractProtocol::s_ptr)>>>
        m_write_dones;

    std::map<std::string, std::function<void(AbstractProtocol::s_ptr)>>
        m_read_dones;

    AbstractCoder *m_coder{nullptr};
};

} // namespace rocket

#endif
